// Description: Single-Page UI for network visualizer.
// Author: keroserene@google.com  (Serene Han)

define(['domReady', 'jquery'], function (domReady, $) {
  var ENTRY_ID_PREFIX = 'v-';
  var BUTTERBAR_TIMEOUT = 1800;
  var NOHOVER_TIMEOUT = 100;

  var gGraphs = {};        // Dict of ID -> Name
  var gSpreadsheets = {};  // Dict of ID -> Spreadsheet Key
  var gCurrentGraphID = 0;
  var gEditMode = false;

  var DOM = {
    view: undefined,
    btnbar: undefined,
  }

  // Hook |entry| (DOM element) to open up a single visualization view.
  function hookGraphEntry(entry, id) {
    var id = entry.id.split('-')[1];
    var name = entry.innerHTML;
    gGraphs[id] = name.trim();
    entry.onclick = function() {
      viewGraph(id);
      $(this).addClass('selected');
    };
  }

  // Helper which loads a url into the AJAX viewport, along with
  // callbacks which take care of some boilerplate. Requires the
  // target URL to have a div #ajax-view.
  function _loadView(url) {
    clearView();
    // TODO(keroserene): Add loading spinners maybe.
    DOM.view.load(url + ' #ajax-view', function() {
      hideButterBar();
    });
    DOM.view.removeClass('hidden');
    DOM.view.show()
  }

  // Display specific visualization, loaded via AJAX.
  function viewGraph(id) {
    window.history.pushState({}, 'unused', '/view/' + id);
    _loadView('/graph/' + id);
    DOM.view.removeClass('hidden');
    DOM.view.show()
    $('#btnbar').show();
    $('#btnbar').removeClass('hidden');
    if (id != gCurrentGraphID) {
      butterBar('Loading...', false);
    }
    gCurrentGraphID = id;
  }

  // When a visualization is created, prepare a new element in the Entries list
  // to indicate a new entry is being created, while awaiting for the actual
  // visualization to be readied.
  function awaitUpdate() {
    $('#graph-list').prepend(
        '<div id="pending" class="graph-entry">...</div>');
  }

  // Fetch JSON data about all existing visualization entries, and update the
  // DOM list if a new one is found.
  function refreshEntries() {
    var pending = $('#pending');
    console.log('fuck');
    $.getJSON('/data.json', function(data) {
      $.each(data, function(i, entry) {
        if (!pending) {
          return;
        }
        var id = entry[0];
        var name = entry[1];
        var spreadsheet = entry[2];
        gSpreadsheets[id] = spreadsheet;
        if (!(id in gGraphs)) {
          gGraphs[id] = name;
          var entryDOM = pending[0];
          pending = null;
          entryDOM.id = ENTRY_ID_PREFIX + id;
          entryDOM.innerHTML = name;
          hookGraphEntry(entryDOM);
        }
      });
      if (pending) {  // Try again if no new entry was found.
        setTimeout(refreshEntries, 500);
      }
    });
  }

  function hideButtonBar() {
    $('#btnbar').addClass('hidden');
    setTimeout(function () { $('#btnbar').hide(); }, 300);
  }

  // Handler for clicking on "CREATE VISUALIZATION".
  function prepareCreateForm() {
    gEditMode = false;
    $('#btn-save').text('Create');
    clearView();
    showHelp();  // Also hides the button bar.
    $('#btn-delete').hide();
    $('.graph-entry').filter('.selected').removeClass('selected');
    // Clear the form.
    $('#id_name').val('');
    $('#id_spreadsheet_link').val('')
    shiftSidebar();
  }

  // Handler for clicking on "EDIT".
  function prepareEditForm() {
    gEditMode = true;
    $('#btn-save').text('Save');
    $('#btn-edit').addClass('disabled');
    $('#btn-delete').show();
    $('#id_name').val(gGraphs[gCurrentGraphID]);
    $('#id_spreadsheet_link').val(
        'https://docs.google.com/a/google.com/spreadsheet/ccc?key=' +
        gSpreadsheets[gCurrentGraphID]);
    $('#id_graph_id').val(gCurrentGraphID);  // For delete requests.
    shiftSidebar();
  }

  // Open the help page via AJAX.
  function showHelp() {
    hideButtonBar();
    DOM.help.removeClass('no-hover');
    DOM.help.removeClass('hidden');
    // _loadView('/help/');
  }
  function hideHelp() {
    DOM.help.addClass('hidden no-hover');
  }

  function saveOrCreate() {
    // TODO(keroserene): Validate spreadsheet format?
    if (!validEntry('id_name', 'Please name your visualization.') ||
        !validEntry('id_spreadsheet_link',
                    'Please provide a valid spreadsheet link.')) {
      return;
    }
    if (gEditMode) {
      updateGraph();
    } else {  // CREATE_MODE
      createGraph();
      awaitUpdate();
    }
    resetSidebar();
  }

  // Validates a form text entry. Returns true if valid, false otherwise.
  function validEntry(entry_id, error_msg) {
    var entry = $('#' + entry_id);
    if (!entry.val()) {
      butterBar(error_msg);
      entry.focus();
      return false;
    }
    return true;
  }

  // Send AJAX POST to create new graph, update local DOM to indicate
  // loading and new graph.
  function createGraph() {
    var data = $('#graph-form').serialize();
    // The trailing slash is /graph/create/ is important.
    $.post('/graph/create/', data, function() {
      butterBar('Finished creation.');
      setTimeout(refreshEntries, 100);
    });
    var name = $('#id_name').val();
    butterBar('Creating new visualization "' + name  + '"...', false);
  }

  // Update the details of a visualization. If the spreadsheet is changed, also
  // reload data.
  function updateGraph() {
    var data = $('#graph-form').serialize();
    var oldSpreadsheet = gSpreadsheets[gCurrentGraphID];
    var newSpreadsheet = $('#id_spreadsheet_link').val().match(/ccc\?key=(.*)/)[1]
    var cachedGraphID = gCurrentGraphID;
    // Trailing slash isvital.
    $.post('/graph/' + gCurrentGraphID + '/update/', data, function() {
      butterBar('Update complete.');
      if (newSpreadsheet != oldSpreadsheet && gCurrentGraphID == cachedGraphID)
        setTimeout(refreshGraph, 100);
    });
    butterBar('Updating details...', false)
    var newName = $('#id_name').val();
    // Rename without bothering with JSON.
    $('#' + ENTRY_ID_PREFIX + gCurrentGraphID).text(newName);
    gGraphs[gCurrentGraphID] = newName;
    gSpreadsheets[gCurrentGraphID] = newSpreadsheet;
  }

  function deleteGraph() {
    var data = $('#graph-form').serialize()
    var name = gGraphs[gCurrentGraphID];
    $.post('/graph/' + gCurrentGraphID + '/delete/', data, function() {
      butterBar('Deleted graph "' + name + '".');
    });
    $('#' + ENTRY_ID_PREFIX + gCurrentGraphID).remove();
    clearView();
    hideButtonBar();
    gCurrentGraphID = null;
    window.history.pushState({}, 'unused', '/');
    resetSidebar();
    butterBar('Deleting "' + name + '"...');
  }

  function refreshGraph() {
    // var data = $('#graph-form').serialize()
    var cachedGraphID = gCurrentGraphID;  // In case user loads another graph.
    $.get('/graph/' + gCurrentGraphID + '/reload/', [], function() {
      console.log('refresh request complete.');
      $('#btn-refresh').removeClass('disabled');
      butterBar('Visualization data refreshed!');
      if (cachedGraphID == gCurrentGraphID) {
        viewGraph(gCurrentGraphID);
      }
    });
    $('#btn-refresh').addClass('disabled');
    hideToolTip($('#tooltip-refresh'));
    butterBar('Refreshing data from spreadsheet...', false);
  }

  function shiftSidebar() {
    $('#sidebar').addClass('widened');
    $('#sidebar-content').addClass('shifted');
    DOM.editPane.show();
  }
  function resetSidebar() {
    $('#sidebar').removeClass('widened');
    $('#sidebar-content').removeClass('shifted');
    DOM.editPane.hide();
    $('#btn-edit').removeClass('disabled');
    hideHelp();
  }

  // Clears everything from the AJAX viewport. Used prior to loading any new
  // view, or when opening the create visualization pane.
  function clearView() {
    $('.graph-entry').filter('.selected').removeClass('selected');
    DOM.view.empty();
    DOM.view.hide();
    if ($('#btn-embed').hasClass('active')) {
      toggleEmbedLink();
    }
  }

  function showToolTip(element) {
    element.removeClass('no-hover');
    setTimeout(function() {
      element.removeClass('hidden');
    }, NOHOVER_TIMEOUT);
  }

  function hideToolTip(element) {
    element.addClass('hidden');
    setTimeout(function() {
      element.addClass('no-hover');
    }, NOHOVER_TIMEOUT);
  }

  // Toggles the embed link. Auto-updates and selects code if needed.
  function toggleEmbedLink() {
    $('#btn-embed').toggleClass('active');
    var embedCode = '<iframe src="' + HOSTNAME + '/graph/' +
                    gCurrentGraphID + '/embed/" ' +
                    'width="1000" height="600"></iframe>';
    var snippet = $('#snippet');
    snippet.text(embedCode);
    snippet.toggleClass('hidden');
    if (!snippet.hasClass('hidden')) {
      snippet.select();
    }
  }

  function butterBar(msg, autohide) {
    if (!msg) return;
    if (undefined === autohide)
      autohide = true;
    var bar = $('#butterbar');
    // console.log(msg);
    bar.html(msg);
    bar.removeClass('hidden');
    if (autohide) {
      setTimeout(function() {
        bar.addClass('hidden');
      }, BUTTERBAR_TIMEOUT);
    }
  }

  function hideButterBar() {
    $('#butterbar').addClass('hidden');
  }

  // Install all AJAX and DOM handlers.
  domReady(function () {
    // Obtain data about all user visualizations in order to populate list.
    $.getJSON('/data.json', function(data) {
      $.each(data, function(i, entry) {
        var id = entry[0];
        var name = entry[1];
        var spreadsheet = entry[2];
        gGraphs[id] = name;
        gSpreadsheets[id] = spreadsheet;
      });
    });

    // Catch DOM references.
    DOM.view = $('#view');
    DOM.help = $('#help');
    DOM.editPane = $('#edit-pane');

    // View handlers.
    $.each($('.graph-entry'), function(i, entry) {
      hookGraphEntry(entry);
    });
    if (GRAPH_ID) {  // If initially loaded from django template.
      gCurrentGraphID = GRAPH_ID;
    }

    // Primary button handlers.
    $('#btn-create').click(prepareCreateForm);
    $('.btn-cancel').click(resetSidebar);
    $('#btn-save').click(saveOrCreate);
    $('#btn-edit').click(prepareEditForm);
    $('#btn-delete').click(deleteGraph);
    $('#btn-refresh').click(refreshGraph);
    $('#btn-embed').click(toggleEmbedLink);

    // Tooltips.
    $('#btn-refresh').hover(
      function() { showToolTip($('#tooltip-refresh')); },
      function() { hideToolTip($('#tooltip-refresh')); });

    // Help
    $('#link-help').click(function(ev) {
      ev.preventDefault();
      showHelp();
    });
  });
});
